const { resolve } = require('path');
const { loadImage } = require('canvas')
const { makePic, test } = require('./pic_font')
const { curl, readdir, copyFile, deleteFile, exists, mkdir, stat, writeFile, cal_media_length, call_ffmpeg, sleep } = require('./util')

const dir_parent_root = '/Users/flyprotoss/work/guoying/stock-video-news-script/temp/'
// const dir_parent_root = 'D:\\work\\stock-video-news-script\\temp';
const api_root = 'http://jfj8.bjyihuilian.com/v1/pic/'
const pic_width = 1280
const pic_height = 720
const media_frame = 15 // 视频帧率
const bengStartSecond = 0.2 // 每场景从第x秒开始蹦字
const bengPerSecond = 0.1 // 每x秒蹦1个字
const fileBgWhitePic60 = resolve(__dirname, 'static', 'bg60.png');
const fileBgWhitePic80 = resolve(__dirname, 'static', 'bg80.png');
const fileBgWhitePic100 = resolve(__dirname, 'static', 'bg100.png');

/**
 * 计算场景所需信息
 */
async function getScenes(item, dir_root) {
    const scenes = []
    // 首幕
    let pathAudio = resolve(dir_root, 'mp3s', `audio_start.mp3`);
    let is_exists = await exists(pathAudio);
    if (!is_exists) pathAudio = ''; // TODO 默认音效
    scenes.push({ title: item.title, date: item.date, company: item.company, analyst: item.analyst, pathAudio, is_first: true });

    for (let j=0; j<item.data.length; j++) {
        // pathVideo/pathImg, pathChart, subtitle, summary, audio(subs)
        let pathVideo = resolve(dir_root, 'pics', `${j+1}-1.mp4`);
        is_exists = await exists(pathVideo);
        if (!is_exists) pathVideo = '';
        let pathImg = resolve(dir_root, 'pics', `${j+1}-1.jpg`);
        is_exists = await exists(pathImg);
        if (!is_exists) pathImg = '';
        let pathChart = resolve(dir_root, 'pics', `${j+1}-1-chart.png`);
        is_exists = await exists(pathChart);
        if (!is_exists) pathChart = '';
        let pathAudio = resolve(dir_root, 'mp3s', `1-${j+1}.mp3`);
        is_exists = await exists(pathAudio);
        if (!is_exists) pathAudio = '';
        let pathSubAudios = [];
        for (let k=1; ;k++) {
            let pathSubAudio = resolve(dir_root, 'mp3s', `1-${j+1}-${k}.mp3`);
            is_exists = await exists(pathSubAudio);
            if (is_exists) pathSubAudios.push(pathSubAudio);
            else break;
        }
        // 中间幕
        scenes.push({
            subtitle: item.data[j].audio_text.replace(/\^/g, '') || '',
            summary: (item.data[j].summary || '').trim(),
            pathVideo,
            pathImg,
            pathChart,
            pathAudio,
            pathSubAudios
        })
    }
    // 尾幕
    pathAudio = resolve(dir_root, 'mp3s', `audio_end.mp3`);
    is_exists = await exists(pathAudio);
    if (!is_exists) pathAudio = ''; // TODO 默认音效
    scenes.push({pathAudio, is_last: true});
    return scenes;
}

/**
 * 视频拆帧
 */
async function videoToPics(pathVideo, destPath, total_pic_count) {
    // ffmpeg -i "test.mp4" -r 15 -q:v 2 -f image2 pic/%3d.jpg
    await call_ffmpeg(`-i "${pathVideo}" -r ${media_frame} -q:v 2 -f image2 ${destPath}/%3d.jpg`);
    // 补足或删除多余图片
    let existVideoPicCount = 0;
    for(let k=1; k<=999; k++) {
        const pic_path = resolve(destPath, (1000 + k + '').substr(1) + '.jpg');
        const is_exist = await exists(pic_path);
        if(is_exist) {
            if(k > total_pic_count) {
                // 删除多余的图片
                await deleteFile(pic_path)
            } else {
                existVideoPicCount++;
            }
        } else {
            if(k <= total_pic_count) {
                // 添加不足的图片
                const pic_source_path = resolve(destPath, (1000 + (k % existVideoPicCount + 1) + '').substr(1) + '.jpg')
                await copyFile(pic_source_path, pic_path)
            }
        }
    }
}

async function getSubtitleIndexs (pathSubAudios, subtitle, total_pic_count) {
    if (!pathSubAudios || pathSubAudios.length < 1) return null;
    const subtitle_list = subtitle.split('|').filter(o => o.trim() !== '').map(o => o.trim());
    const subtitle_pic_indexs = [];
    // 计算每句话图片数
    for(let i=0; i<subtitle_list.length; i++){
        const subtitle_seconds = await cal_media_length(pathSubAudios[i]);
        const subtitle_pic_count = Math.round(subtitle_seconds * media_frame);
        subtitle_pic_indexs.push(i===0? subtitle_pic_count : subtitle_pic_count + subtitle_pic_indexs[i-1]);
    }
    subtitle_pic_indexs[subtitle_pic_indexs.length-1] = total_pic_count;
    return subtitle_pic_indexs.map((o,i) => ({ subtitle: subtitle_list[i], index: o}));
}

// let lastFrameInfo = null;
function getFrameInfo(data) {
    const { currLv, totalLv, destPath, width, height, bgImg, imgChart, imgChartBg, subtitle, summary, beng, is_first, is_last, title, date, company, analyst } = data;
    const retVal = { destPath, width, height, bgImg, imgChart, imgChartBg, subtitle, summary, beng };
    if (is_first) {
        return Object.assign(retVal, { title, date, company, analyst, is_first });
    } else if (is_last) {
        return Object.assign(retVal, { title, company: '谢谢观看', is_last });
    } else {
        // lastFrameInfo = retVal;
        return retVal;
    }
}

function compareFrameInfo (o1, o2) {
    if(Object.keys(o1).length !== Object.keys(o2).length) return false;
    for (let k of Object.keys(o1)) {
        // 除了目标path之外一样
        if (k === 'destPath') continue;
        if (o1[k] !== o2[k]) return false;
    }
    return true;
}

(async () => {
    // await test();
    // await makePic(JSON.parse('{"destPath":"/Users/flyprotoss/work/guoying/node_video_script/test/002.jpg","width":1280,"height":720,"bgImg":"/Users/flyprotoss/work/guoying/node_video_script/test/0021.jpg","imgChart":null,"imgChartBg":{"onload":null,"onerror":null},"subtitle":"预计公司2020-22年的归母净利润为51.21/60.82/73.93亿元，对应的EPS为1.88/2.23/2.71元","summary":"预计公司2020-22年的归母净利润为51.21 / 60.82 / 73.93亿元，对应的EPS为1.88 / 2.23 / 2.71元","beng":"预计公司2020-22年的归母净利润为51.21 / 60.82 "}'))
    // return;

    while (true) {
        console.log('.')
        const json = await curl(`${api_root}get_news_scripts_by_status/6`);
        const list = JSON.parse(json);
        for(let i=0; i<list.length; i++){
            const cal_start = new Date().getTime();
            const item_json = await curl(`${api_root}get_news_script/${list[i].id}`)
            const item = JSON.parse(JSON.parse(item_json).data)
            const dir_root = resolve(dir_parent_root, item.id.toString())

            // 构建场景
            const scenes = await getScenes(item, dir_root);

            // 构建关键帧
            let imgChartBg = await loadImage(fileBgWhitePic80);
            for (let j=0; j<scenes.length; j++) {
                // if (j !== 11) continue; // TODO 测试移除
                const scene = scenes[j];
                const destPath = resolve(dir_root, j.toString());
                await mkdir(destPath);
                // 计算场景时长
                const seconds = await cal_media_length(scene.pathAudio);
                scene.seconds = seconds;
                // 此幕需要图片的数量
                const total_pic_count = Math.round(seconds * media_frame);
                scene.total_pic_count = total_pic_count;
                // 视频->拆帧
                if (scene.pathVideo) {
                    await videoToPics(scene.pathVideo, destPath, total_pic_count);
                }
                // 图表
                let imgChart = null;
                if (scene.pathChart) {
                    imgChart = await loadImage(scene.pathChart);
                }
                let subtitleAndIndexs = await getSubtitleIndexs(scene.pathSubAudios, scene.subtitle, total_pic_count);
                let tmpFrameInfo = {};
                for (let v=1; v<=total_pic_count; v++) {
                    let destPicPath = resolve(destPath, `${(1000+v).toString().substr(1)}.jpg`);
                    let bgImg = (scene.pathVideo ? destPicPath : scene.pathImg);
                    let subtitle = scene.subtitle;
                    if (subtitleAndIndexs != null) {
                        // 此场景需要显示多条字幕
                        let index = 0;
                        while (index < subtitleAndIndexs.length) {
                            if (v<=subtitleAndIndexs[index].index)
                                break;
                            index++;
                        }
                        // console.log(subtitle, index, subtitleAndIndexs)
                        subtitle = subtitleAndIndexs[index].subtitle;
                    }
                    const is_video = scene.pathVideo != null && scene.pathVideo.length > 0;
                    // 蹦字：summary从1s开始，每0.5s蹦出一个
                    let beng = '';


                    if (scene.summary) {
                        const bengLen = Math.floor((v - bengStartSecond * media_frame)/Math.ceil(media_frame * bengPerSecond));
                        if (bengLen > 0) {
                            // 18 -> 1
                            // 24 -> 2
                            // 30 -> 3
                            beng = scene.summary.substr(0, bengLen);
                        }
                    }

                    const frameInfo = getFrameInfo({
                        currLv:v,
                        totalLv:total_pic_count,
                        destPath: destPicPath,
                        width:pic_width,
                        height:pic_height,
                        bgImg,
                        imgChart: (v <= media_frame || v >= total_pic_count - media_frame ? null : imgChart), imgChartBg,
                        subtitle,
                        beng,
                        summary: scene.summary,
                        is_first: scene.is_first || false,
                        is_last: scene.is_last || false,
                        title: item.title, date: item.date, company: item.company, analyst: item.analyst
                    });
                    // 合成帧
                    // title: item.title, date: item.date, company: item.company, analyst: item.analyst
                    if (!compareFrameInfo(frameInfo, tmpFrameInfo)) {
                        // 信息不相同，才合成帧，相同的直接复制->见下面补图代码
                        // if (frameInfo.subtitle && frameInfo.subtitle.indexOf('考虑到公司国内新能源整车龙头地位稳固')>-1)
                        //     console.log(frameInfo)
                        await makePic(frameInfo);
                        tmpFrameInfo = frameInfo;
                    }
                }
                // 复制音频
                await copyFile(scene.pathAudio, resolve(destPath, '0.mp3'));
                // console.log(scene)
            }

            // 补图
            for (let j=0; j<scenes.length; j++) {
                const scene = scenes[j]
                let lastExistFile = '';
                for (let v=1; v<=scene.total_pic_count; v++) {
                    let destPicPath = resolve(resolve(dir_root, j.toString()), `${(1000 + v).toString().substr(1)}.jpg`);
                    let is_exist = await exists(destPicPath);
                    if (is_exist) lastExistFile = destPicPath;
                    else {
                        await copyFile(lastExistFile, destPicPath);
                    }
                }
            }

            // 帧->合成视频
            let files_txt_content = '';
            for (let j=0; j<scenes.length; j++) {
                // 图片合成视频 -> 0.mp4
                await call_ffmpeg(`-r ${media_frame} -i ${resolve(dir_root, j.toString(), '%03d.jpg')} ${resolve(dir_root, j.toString(), '0.mp4')} -y`);
                // 视频合成声音
                const dest_video_file = resolve(dir_root, j.toString(), '00.mp4')
                await deleteFile(dest_video_file)
                // 合并声音 -> 00.mp4:   ffmpeg -i 0.mp4 -i 0.mp3 00.mp4
                await call_ffmpeg(`-i ${resolve(dir_root, j.toString(), '0.mp4')} -i ${resolve(dir_root, j.toString(), '0.mp3')} ${resolve(dir_root, j.toString(), '00.mp4')}`)
                files_txt_content += `file '${dir_root}/${j}/00.mp4'\n`

                // lv, currLv, totalLv, destPath, width, height,
                // bgImg, imgChartBg, imgChart, subtitle, summary, first: {...}
            }

            // 组合成一个视频
            const file = resolve(dir_root, 'files.txt')
            await writeFile(file, files_txt_content)
            // ffmpeg -f concat -safe 0 -i files.txt -c copy -y out-1.mp4
            await deleteFile(resolve(dir_root, 'out.mp4'))

            const _files = resolve(dir_root, 'files.txt')
            const _out_720_mp4 = resolve(dir_root, 'out_720.mp4');
            await call_ffmpeg(`-f concat -safe 0 -i ${_files} -c copy -y ${_out_720_mp4}`)

            // 转成720p
            // ffmpeg -i out0.mp4 -vb 500k -vcodec h264 -r 15 -s 1280x720 -ar 44100 -ac 2 -ab 96k -acodec aac out.mp4 -y
            const _out_mp4 = _out_720_mp4.replace('out_720', 'out')
            await call_ffmpeg(`-i ${_out_720_mp4} -vb 500k -vcodec h264 -r ${media_frame} -s 1280x720 -ar 44100 -ac 2 -ab 96k -acodec aac ${_out_mp4} -y`)

            // 上传 转 python

            // 更新每个场景的时长
            const lengths = scenes.map(o => o.seconds.toString()).map(o => o.length > 5 ? o.substr(0, 5) : o).join('@')
            await curl(`${api_root}edit_news_script_length/${item.id}/${lengths}`);
            // 更新状态：待上传
            await curl(`${api_root}edit_news_scripts_status/${item.id}/7`);
            const cal_end = new Date().getTime();
            console.log('finished...' + item.id + '...' + (cal_end - cal_start)/1000 + 's')
        }
        await sleep(5000)
    }
})();
